1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.util.data_structures; 12 13 public import hip.util.string: String; 14 struct Pair(A, B, string aliasA = "", string aliasB = "") 15 { 16 17 A first; 18 B second; 19 20 alias a = first; 21 alias b = second; 22 23 static if(aliasA != "") 24 mixin("alias "~aliasA~" = first;"); 25 static if(aliasB != "") 26 mixin("alias "~aliasB~" = second;"); 27 } 28 struct Dirty(T) 29 { 30 T value; 31 private bool isDirty; 32 33 auto opAssign(T value) 34 { 35 if(value != this.value) this.value = value, isDirty = true; 36 return this; 37 } 38 void clearDirtyFlag(){isDirty = false;} 39 } 40 41 mixin template DirtyFlagFields(string flagName, T, string[] fields) 42 { 43 static foreach(f; fields) 44 { 45 mixin("T _",f,";", 46 "T ",f,"() => _",f,";", 47 "T ",f,"(T v){if(_",f," != v) ",flagName," = true; return _",f," = v;}"); 48 } 49 } 50 51 struct Tuple(Fields...) 52 { 53 Fields fields; 54 alias fields this; 55 pragma(inline, true) size_t length(){ return Fields.length; } 56 } 57 auto tuple(Args...)(Args a){ return Tuple!(Args)(a); } 58 59 pragma(LDC_no_typeinfo) 60 struct DirtyFlagsCmp(alias flag, Fields...) 61 { 62 import std.typecons; 63 static if(is(__traits(parent, Fields[0]) == struct)) 64 private static alias P = __traits(parent, Fields[0])*; 65 else 66 private static alias P = __traits(parent, Fields[0]); 67 P parent; 68 Tuple!(typeof(Fields)) base; 69 70 this(P parent) 71 { 72 start(parent); 73 } 74 75 void start(P parent) 76 { 77 this.parent = parent; 78 static foreach (i, f; Fields) 79 base[i] = __traits(child, parent, f); 80 } 81 82 void update() 83 { 84 bool changed; 85 static foreach (i, f; Fields) 86 { 87 { 88 alias T = typeof(f); 89 changed = changed || (__traits(child, parent, f) !is base[i]); 90 } 91 } 92 __traits(child, parent, flag) = __traits(child, parent, flag) || changed; 93 } 94 alias opCall = update; 95 } 96 97 98 99 /** 100 * RangeMap allows specifying a range in which a value spams, quite useful for defining outcomes 101 * based on an input, experience gain progression, etc. Example Usage: 102 * ```d 103 * RangeMap!(int, string) colorRanges = "White"; //Default is "White" 104 * colorRanges[0..9] = "Red"; 105 * colorRanges[10..19] = "Green"; 106 * colorRanges[20..29] = "Blue" 107 * 108 * writeln(colorRanges[5]); //Prints "Red" 109 * ``` 110 */ 111 struct RangeMap(K, V) 112 { 113 import hip.util.reflection:isNumeric; 114 @nogc: 115 static assert(isNumeric!K, "RangeMap key must be a numeric type"); 116 protected Array!K ranges; 117 protected Array!V values; 118 protected V _default; 119 120 /** 121 * When the value is out of range, the value returned is the _default one. 122 */ 123 ref RangeMap setDefault(V _default) 124 { 125 this._default = _default; 126 return this; 127 } 128 /** 129 * Alternative to the slice syntax 130 */ 131 ref RangeMap setRange(K a, K b, V value) 132 { 133 if(ranges == null) 134 { 135 ranges = Array!K(8); 136 values = Array!V(8); 137 } 138 int rLength = cast(int)ranges.length; 139 ranges.reserve(ranges.length+2); 140 if(a > b) 141 { 142 K temp = a; 143 a = b; 144 b = temp; 145 } 146 if(rLength != 0 && ranges[rLength-1] > a) //Silently ignore for now 147 return this; 148 ranges~=a; 149 ranges~=b; 150 values~=value; 151 return this; 152 } 153 154 /** 155 * Uses binary search for finding the value range. 156 */ 157 V get(K value) 158 { 159 int l = 0; 160 int length = cast(int)ranges.length; 161 int r = length-1; 162 163 while(l < r) 164 { 165 int m = cast(int)((l+r)/2); 166 if(m % 2 != 0) 167 m--; 168 K k = ranges[m]; 169 //Check if value is between a[m] and a[m-1] 170 if(m+1 < length && value >= k && value <= ranges[m+1]) 171 return values[cast(int)(m/2)]; 172 else if(k < value) 173 l = m + 1; 174 else if(m > value) 175 r = m - 1; 176 //Check if both values on the right is greater than value 177 else if(m+1 < length && k > value && ranges[m+1] > value) 178 break; 179 } 180 return _default; 181 } 182 183 pragma(inline) auto ref opAssign(V value) 184 { 185 setDefault(value); 186 return this; 187 } 188 pragma(inline) V opSliceAssign(V value, K start, K end) 189 { 190 setRange(start, end, value); 191 return value; 192 } 193 pragma(inline) V opIndex(K index){return get(index);} 194 } 195 /** 196 * RefCounted, Array @nogc, OutputRange compatible, it aims to bring the same result as one would have by using 197 * int[], Array!int should be equivalent, any different behaviour should be contacted. 198 * It may use more memory than requested for not making reallocation a big bottleneck 199 */ 200 pragma(LDC_no_typeinfo) 201 struct Array(T) 202 { 203 size_t length; 204 T* data; 205 private size_t capacity; 206 private int* countPtr; 207 import core.stdc.stdlib:malloc, calloc; 208 import core.stdc.string:memcpy, memset; 209 import core.stdc.stdlib:realloc; 210 211 this(this) @nogc 212 { 213 if(countPtr !is null) 214 *countPtr = *countPtr + 1; 215 } 216 217 pragma(inline) int opApply(scope int delegate(size_t idx, ref T) dg) 218 { 219 int result = 0; 220 for(int i = 0; i < length; i++) 221 { 222 result = dg(i, data[i]); 223 if(result) 224 break; 225 } 226 return result; 227 } 228 229 pragma(inline) int opApply(scope int delegate(ref T) dg) 230 { 231 int result = 0; 232 for(int i = 0; i < length; i++) 233 { 234 result = dg(data[i]); 235 if(result) break; 236 } 237 return result; 238 } 239 private void initialize(size_t capacity) @nogc 240 { 241 this.data = cast(T*)calloc(capacity, T.sizeof); 242 this.capacity = capacity; 243 this.countPtr = cast(int*)malloc(int.sizeof); 244 *this.countPtr = 1; 245 this[0..capacity] = T.init; 246 } 247 248 static Array!T opCall(size_t capacity = 1) @nogc 249 { 250 Array!T ret; 251 ret.initialize(capacity); 252 return ret; 253 } 254 255 static Array!T opCall(size_t length, T value) @nogc 256 { 257 Array!T ret = Array!(T)(length); 258 ret.length = length; 259 ret[] = value; 260 return ret; 261 } 262 263 static Array!T opCall(scope T[] arr) @nogc 264 { 265 Array!T ret = Array!(T)(arr.length); 266 ret.length = arr.length; 267 ret.data[0..ret.length] = arr[]; 268 return ret; 269 } 270 271 void* getRef() 272 { 273 *countPtr = *countPtr + 1; 274 return cast(void*)&this; 275 } 276 277 pragma(inline) bool opEquals(R)(const R other) const 278 if(is(R == typeof(null))) 279 { 280 return data == null; 281 } 282 void dispose() @nogc 283 { 284 import core.stdc.stdlib:free; 285 free(data); 286 free(countPtr); 287 } 288 immutable(T*) ptr(){return cast(immutable(T*))data;} 289 size_t opDollar() @nogc {return length;} 290 291 T[] opSlice() @nogc 292 { 293 return data[0..length]; 294 } 295 296 T[] opSlice(size_t start, size_t end) @nogc 297 { 298 return data[start..end]; 299 } 300 auto ref opSliceAssign(T)(T value) @nogc 301 { 302 data[0..length] = value; 303 return this; 304 } 305 auto ref opSliceAssign(T)(T value, size_t start, size_t end) @nogc 306 { 307 data[start..end] = value; 308 return this; 309 } 310 import hip.util.reflection:isArray; 311 auto ref opAssign(Q)(Q value) @nogc 312 if(isArray!Q) 313 { 314 if(data == null) 315 data = cast(T*)malloc(T.sizeof*value.length); 316 else 317 data = cast(T*)realloc(data, T.sizeof*value.length); 318 length = value.length; 319 capacity = value.length; 320 memcpy(data, value.ptr, T.sizeof*value.length); 321 return this; 322 } 323 324 ref auto opIndex(size_t index) @nogc 325 { 326 assert(index>= 0 && index < length, "Array out of bounds"); 327 return data[index]; 328 } 329 330 pragma(inline) private void resize(uint newSize) @nogc 331 { 332 if(data == null || capacity == 0) 333 initialize(newSize); 334 else 335 { 336 data = cast(T*)realloc(data, newSize*T.sizeof); 337 capacity = newSize; 338 } 339 } 340 ///Guarantee that no allocation will occur for the specified reserve amount of memory 341 void reserve(size_t newSize){if(newSize > capacity)resize(cast(uint)newSize);} 342 auto ref opOpAssign(string op, Q)(Q value) @nogc if(op == "~") 343 { 344 if(*countPtr != 1) 345 { 346 //Save old data 347 T* oldData = data; 348 //Remove from the ref counter 349 *countPtr = *countPtr - 1; 350 //Re initialize 351 initialize(length); 352 memcpy(data, oldData, T.sizeof*length); 353 } 354 static if(is(Q == T)) 355 { 356 if(data == null) 357 initialize(1); 358 if(length + 1 >= capacity) 359 resize(cast(uint)((length+1)*1.5)); 360 data[length++] = value; 361 } 362 else static if(is(Q == T[]) || is(Q == Array!T)) 363 { 364 if(data == null) 365 initialize(value.length); 366 if(length + value.length >= capacity) 367 resize(cast(uint)((length+value.length)*1.5)); 368 memcpy(data+length, value.ptr, T.sizeof*value.length); 369 length+= value.length; 370 } 371 return this; 372 } 373 374 // String asString() @nogc 375 // { 376 // return String(this[0..$]); 377 // } 378 void put(T data) @nogc {this~= data;} 379 ~this() @nogc 380 { 381 if(countPtr != null) 382 { 383 *countPtr = *countPtr - 1; 384 assert(*countPtr >= 0); 385 if(*countPtr == 0) 386 dispose(); 387 data = null; 388 countPtr = null; 389 length = 0; 390 } 391 } 392 } 393 394 395 private mixin template Array2DMixin(T) 396 { 397 int opApply(scope int delegate(ref T) dg) 398 { 399 int result = 0; 400 for(int i = 0; i < width*height; i++) 401 { 402 result = dg(data[i]); 403 if (result) 404 break; 405 } 406 return result; 407 } 408 409 private uint height; 410 private uint width; 411 @nogc int getWidth() const {return width;} 412 @nogc int getHeight() const {return height;} 413 @nogc T[] opSlice(size_t start, size_t end) 414 { 415 if(end < start) 416 { 417 size_t temp = end; 418 end = start; end = temp; 419 } 420 if(end > width*height) 421 end = width*height; 422 return data[start..end]; 423 } 424 pragma(inline, true) 425 { 426 @nogc ref auto opIndex(size_t i, size_t j){return data[i*width+j];} 427 @nogc ref auto opIndex(size_t i) 428 { 429 size_t temp = i*width; 430 return data[temp..temp+width]; 431 } 432 @nogc auto opIndexAssign(T)(T value, size_t i, size_t j){return data[i*width+j] = value;} 433 @nogc size_t opDollar() const {return width*height;} 434 @nogc bool opCast() const{return data !is null;} 435 } 436 } 437 438 /** 439 * By using Array2D instead of T[][], only one array instance is created, not "n" arrays. So it is a lot 440 * faster when you use that instead of array of arrays. 441 * 442 * Its main usage is for avoiding a[i][j] static array, and not needing to deal with array of arrays. 443 */ 444 pragma(LDC_no_typeinfo) 445 struct Array2D(T) 446 { 447 448 mixin Array2DMixin!T; 449 Array2D_GC!T toGC() 450 { 451 Array2D_GC!T ret = new Array2D_GC!T(width, height); 452 ret.data[0..width*height] = data[0..width*height]; 453 destroy(this); 454 455 return ret; 456 } 457 @nogc: 458 private T* data; 459 import core.stdc.stdlib; 460 461 private int* countPtr; 462 463 this(this){*countPtr = *countPtr + 1;} 464 this(uint lengthHeight, uint lengthWidth) 465 { 466 data = cast(T*)malloc((lengthHeight*lengthWidth)*T.sizeof); 467 countPtr = cast(int*)malloc(int.sizeof); 468 *countPtr = 0; 469 height = lengthHeight; 470 width = lengthWidth; 471 } 472 ~this() 473 { 474 if(countPtr == null) 475 return; 476 *countPtr = *countPtr - 1; 477 if(*countPtr <= 0) 478 { 479 free(data); 480 free(countPtr); 481 data = null; 482 countPtr = null; 483 width = height = 0; 484 } 485 } 486 487 488 } 489 490 class Array2D_GC(T) 491 { 492 private T[] data; 493 this(uint lengthHeight, uint lengthWidth) 494 { 495 data = new T[](lengthHeight*lengthWidth); 496 width = lengthWidth; 497 height = lengthHeight; 498 } 499 mixin Array2DMixin!T; 500 } 501 502 private uint hash_fnv1(T)(T value) 503 { 504 import hip.util.reflection:isArray, isPointer; 505 enum fnv_offset_basis = 0x811c9dc5; 506 enum fnv_prime = 0x01000193; 507 508 byte[] data; 509 static if(isArray!T) 510 data = (cast(byte*)value.ptr)[0..value.length*T.sizeof]; 511 else static if(is(T == interface) || is(T == class) || isPointer!T) 512 data = cast(byte[])(cast(void*)value)[0..(void*).sizeof]; 513 else //Value types: int, float, struct, etc 514 data = (cast(byte*)&value)[0..T.sizeof]; 515 516 typeof(return) hash = fnv_offset_basis; 517 518 foreach(byteFromData; data) 519 { 520 hash*= fnv_prime; 521 hash^= byteFromData; 522 } 523 return hash; 524 } 525 526 struct Map(K, V) 527 { 528 import core.stdc.stdlib; 529 static enum initializationLength = 128; 530 private static enum increasingFactor = 1.5; 531 private static enum increasingThreshold = 0.7; 532 private alias hashFunc = hash_fnv1; 533 534 private struct MapData 535 { 536 K key; 537 V value; 538 } 539 private struct MapBucket 540 { 541 MapData data; 542 MapBucket* next; 543 } 544 private Array!MapBucket dataSet; 545 546 private int* countPtr; 547 548 this(this) 549 { 550 if(countPtr !is null) 551 *countPtr = *countPtr + 1; 552 } 553 554 private int entriesCount; 555 556 private void initialize() @nogc 557 { 558 dataSet = Array!(MapBucket)(initializationLength); 559 dataSet.length = initializationLength; 560 dataSet[] = MapBucket.init; 561 countPtr = cast(int*)malloc(int.sizeof); 562 *countPtr = 0; 563 } 564 static auto opCall() @nogc 565 { 566 Map!(K, V) ret; 567 ret.initialize(); 568 return ret; 569 } 570 571 572 573 void clear() @nogc 574 { 575 entriesCount = 0; 576 for(int i = 0; i < dataSet.length; i++) 577 { 578 if(dataSet[i] != MapBucket.init) 579 { 580 MapBucket* buck = dataSet[i].next; 581 while(buck != null) 582 { 583 MapBucket copy = *buck; 584 free(buck); 585 buck = copy.next; 586 } 587 } 588 } 589 } 590 //Called when array is filled at least increasingThreshold 591 private void recalculateHashes() @nogc 592 { 593 size_t newSize = cast(size_t)(dataSet.capacity * increasingFactor); 594 Array!MapBucket newArray = Array!MapBucket(newSize); 595 596 for(int i = 0; i < dataSet.length; i++) 597 { 598 if(dataSet[i] != MapBucket.init) 599 { 600 newArray[hashFunc(dataSet[i].data.key) % newSize] = dataSet[i]; 601 } 602 } 603 604 dataSet = newArray; 605 } 606 607 bool has(K key) @nogc {return dataSet[getIndex(key)] != MapBucket.init;} 608 pragma(inline) uint getIndex(K key) @nogc {return hashFunc(key) % dataSet.length;} 609 610 611 ref auto opIndex(K index) @nogc 612 { 613 if(dataSet.length == 0) 614 initialize(); 615 return dataSet[getIndex(index)].data.value; 616 } 617 ref auto opIndexAssign(V value, K key) @nogc 618 { 619 if(dataSet.length == 0) 620 initialize(); 621 uint i = getIndex(key); 622 //Unexisting bucket 623 if(dataSet[i] == MapBucket.init) 624 { 625 entriesCount++; 626 dataSet[i] = MapBucket(MapData(key, value), null); 627 if(entriesCount > dataSet.length * increasingThreshold) 628 recalculateHashes(); 629 } 630 else //Existing bucket 631 { 632 MapBucket* curr = &dataSet[i]; 633 do 634 { 635 //Check if the key is the same as the other. 636 if(curr.data.key == key) 637 { 638 curr.data.value = value; 639 return this; 640 } 641 else if(curr.next != null) 642 curr = curr.next; 643 } 644 while(curr.next != null); 645 curr.next = cast(MapBucket*)malloc(MapBucket.sizeof); 646 *curr.next = MapBucket(MapData(key, value), null); 647 648 } 649 return this; 650 } 651 652 auto opBinaryRight(string op, L)(const L lhs) const @nogc 653 if(op == "in") 654 { 655 if(dataSet.length == 0) 656 initialize(); 657 uint i = getIndex(key); 658 if(dataSet[i] == MapBucket.init) 659 return null; 660 return &dataSet[i]; 661 } 662 663 ~this() @nogc 664 { 665 if(countPtr !is null) 666 { 667 *countPtr = *countPtr - 1; 668 if(*countPtr == 0) 669 { 670 //Dispose 671 clear(); 672 free(countPtr); 673 } 674 countPtr = null; 675 } 676 } 677 678 } 679 alias AArray = Map; 680 681 682 /** 683 * High efficient(at least memory-wise), tightly packed Input queue that supports any kind of data in 684 * a single allocated memory pool(no fragmentation). 685 * 686 * This class could probably be extendeded to be every event handler 687 */ 688 class EventQueue 689 { 690 import hip.util.memory; 691 @nogc: 692 693 struct Event 694 { 695 ubyte type; 696 ubyte evSize; 697 void[0] evData; 698 } 699 700 ///Linearly allocated variable length Events 701 void* eventQueue; 702 ///BytesOffset should never be greater than capacity 703 uint bytesCapacity; 704 uint bytesOffset; 705 protected uint pollCursor; 706 707 protected this(uint capacity) 708 { 709 ///Uses capacity*greatest structure size 710 bytesCapacity = cast(uint)capacity; 711 bytesOffset = 0; 712 eventQueue = malloc(bytesCapacity); 713 } 714 715 716 void post(T)(ubyte type, T ev) 717 { 718 assert(bytesOffset+T.sizeof+Event.sizeof < bytesCapacity, "InputQueue Out of bounds"); 719 if(pollCursor == bytesOffset) //It will restart if everything was polled 720 { 721 pollCursor = 0; 722 bytesOffset = 0; 723 } 724 else if(pollCursor != 0) //It will copy everything from the right to the start 725 { 726 memcpy(eventQueue, eventQueue+pollCursor, bytesOffset-pollCursor); 727 //Compensates the offset into the cursor. 728 bytesOffset-= pollCursor; 729 //Restarts the poll cursor, as everything from the right moved to left 730 pollCursor = 0; 731 } 732 Event temp; 733 temp.type = type; 734 temp.evSize = T.sizeof; 735 memcpy(eventQueue+bytesOffset, &temp, Event.sizeof); 736 memcpy(eventQueue+bytesOffset+Event.sizeof, &ev, T.sizeof); 737 bytesOffset+= T.sizeof+Event.sizeof; 738 } 739 740 void clear() 741 { 742 //By setting it equal, no one will be able to poll it 743 pollCursor = bytesOffset; 744 } 745 746 Event* poll() 747 { 748 if(bytesOffset - pollCursor <= 0) 749 return null; 750 Event* ev = cast(Event*)(eventQueue+pollCursor); 751 pollCursor+= ev.evSize+Event.sizeof; 752 return ev; 753 } 754 755 protected ~this() 756 { 757 free(eventQueue); 758 eventQueue = null; 759 pollCursor = 0; 760 bytesCapacity = 0; 761 bytesOffset = 0; 762 } 763 } 764 765 class Node(T) 766 { 767 T data; 768 Node!(T)[] children; 769 Node!T parent; 770 771 this(T data){this.data = data;} 772 773 pragma(inline) bool isRoot() const @nogc nothrow {return parent is null;} 774 pragma(inline) bool hasChildren() const @nogc nothrow {return children.length != 0;} 775 Node!T get(T data) 776 { 777 foreach(node; this) 778 { 779 if(node.data == data) 780 return node; 781 } 782 return null; 783 } 784 pragma(inline) Node!T addChild(T data) 785 { 786 Node!T ret = new Node(data); 787 ret.parent = this; 788 children~= ret; 789 return ret; 790 } 791 792 Node!T getRoot() 793 { 794 Node!T ret = this; 795 while(ret.parent !is null) 796 ret = ret.parent; 797 return ret; 798 } 799 800 int opApply(scope int delegate(Node!T) cb) 801 { 802 foreach(child; children) 803 { 804 if(cb(child) || child.opApply(cb)) 805 return 1; 806 } 807 return 0; 808 } 809 } 810 811 struct Signal(A...) 812 { 813 Array!(void function(A)) listeners; 814 void dispatch(A a) 815 { 816 foreach (void function(A) l; listeners) 817 l(a); 818 } 819 } 820 821 /** 822 * Basically same code from std.array.staticArray but no need to import std. 823 * Use static arrays whenever needing to "fire-and-forget" small arrays 824 */ 825 T[N] staticArray(T, size_t N)(auto ref T[N] arr){return arr;}